#include "Level.h"
#include "ConfigurationManager.h"

#include <iostream>
#include <time.h>

Level::Level(void)
{
	//Initialize grid of level elements
	for(int i=0; i < Level_MaxLevelGridSize; i++) for(int j=0; j < Level_MaxLevelGridSize; j++)
	{
		_GridLevelElement[i][j] = NULL;
		_GridLevelElementToRefresh[i][j] = false;
	}

	//Initialize level grid size
	_GridLevelSize = sf::Vector2i(0,0);
}

Level::~Level(void)
{
	//Delete list of actors
	for(std::list<Actor*>::iterator i = _ListActor.begin();
		i != _ListActor.end(); ++i)
	delete *i;

	//Delete list of level elements
	for(int i=0; i <= _GridLevelSize.y; i++) for(int j=0; j <= _GridLevelSize.x; j++)
		if(_GridLevelElement[i][j] != NULL)
		{
			delete _GridLevelElement[i][j];
			_GridLevelElement[i][j] = NULL;
		}
}

void Level::Think()
{	
	//Think Level elements
	for(int i=0; i <= _GridLevelSize.y; i++) for(int j=0; j <= _GridLevelSize.x; j++)
		if(_GridLevelElement[i][j] != NULL)
			(_GridLevelElement[i][j])->Think();

	//Think Actors
	for(std::list<Actor*>::iterator i = _ListActor.begin(); 
		i != _ListActor.end(); ++i)
		(*i)->Think();
}

void Level::Draw(sf::RenderWindow &pRenderWindow)
{
	//Draw Level elements
	for(int i=0; i <= _GridLevelSize.y; i++) for(int j=0; j <= _GridLevelSize.x; j++)
		if(_GridLevelElementToRefresh[i][j])
		{
			sf::Shape vClearZone = sf::Shape::Rectangle(
				(float)j*Conf_LevelElementSize,
				(float)i*Conf_LevelElementSize,
				(float)(j+1)*Conf_LevelElementSize,
				(float)(i+1)*Conf_LevelElementSize,
				sf::Color(148,137,111));
			pRenderWindow.Draw(vClearZone);
			if(_GridLevelElement[i][j] != NULL)
				(_GridLevelElement[i][j])->Draw(pRenderWindow);

			SetAsRefreshed(j, i);
		}

	//Draw Actors
	for(std::list<Actor*>::iterator i = _ListActor.begin(); 
		i != _ListActor.end(); ++i)
		(*i)->Draw(pRenderWindow);
}

void Level::DrawAll(sf::RenderWindow &pRenderWindow)
{
	pRenderWindow.Clear(sf::Color(148,137,111));

	//Draw Level elements
	for(int i=0; i <= _GridLevelSize.y; i++) for(int j=0; j <= _GridLevelSize.x; j++)
		if(_GridLevelElement[i][j] != NULL)
			(_GridLevelElement[i][j])->Draw(pRenderWindow);

	//Draw Actors
	for(std::list<Actor*>::iterator i = _ListActor.begin(); 
		i != _ListActor.end(); ++i)
		(*i)->Draw(pRenderWindow);
}

void Level::PushLevelElementInGrid(LevelElement *pLevelElement)
{
	int vX = pLevelElement->GetX() / Conf_LevelElementSize;
	int vY = pLevelElement->GetY() / Conf_LevelElementSize;

	if(vX < Level_MaxLevelGridSize && vY < Level_MaxLevelGridSize)
	{
		_GridLevelElement[vY][vX] = pLevelElement;
		if(vX > _GridLevelSize.x) _GridLevelSize.x = vX;
		if(vY > _GridLevelSize.y) _GridLevelSize.y = vY;
	}
	else
	{
		std::cerr << "Level element at coordinate " << pLevelElement->GetX() << " " << pLevelElement->GetY() << " is out of range!"<<std::endl;
	}
}

int Level::IsOnLevelGridX(Actor& pActor) { return !(pActor.GetX()%Conf_LevelElementSize); }

int Level::IsOnLevelGridY(Actor& pActor) { return !(pActor.GetY()%Conf_LevelElementSize); }

int Level::IsOnLevelGrid(Actor& pActor) { return IsOnLevelGridX(pActor) && IsOnLevelGridY(pActor); }

void Level::RefreshRequest(Actor& pActor)
{
	int vGridX = pActor.GetX()/Conf_LevelElementSize;
	int vGridY = pActor.GetY()/Conf_LevelElementSize;

	_GridLevelElementToRefresh[vGridY][vGridX] = true;

	if(!IsOnLevelGridX(pActor)) _GridLevelElementToRefresh[vGridY][vGridX + 1] = true;
	if(!IsOnLevelGridY(pActor)) _GridLevelElementToRefresh[vGridY + 1][vGridX] = true;
}

LevelElement* Level::SearchLevelElement(int pX, int pY )
{
	if(pX%20 != 0 || pY%20 != 0)
		return NULL;

	return GetLevelElementInGrid(pX/Conf_LevelElementSize, pY/Conf_LevelElementSize);
}

LevelElement* Level::SearchLevelElementFromActor(Actor& pActor, ActorOrientation pOrientation)
{
	if( IsOnLevelGrid(pActor) ) 
		switch(pOrientation)
		{
			case Right:
				return SearchLevelElement(pActor.GetX() + Conf_LevelElementSize, pActor.GetY());
			case Down:
				return SearchLevelElement(pActor.GetX(), pActor.GetY() + Conf_LevelElementSize);
			case Left:
				return SearchLevelElement(pActor.GetX() - Conf_LevelElementSize, pActor.GetY());
			case Up:
				return SearchLevelElement(pActor.GetX(), pActor.GetY() - Conf_LevelElementSize);
		}

	return NULL;
}

int Level::CanTakeOrientation(ActorOrientation pOrientation, Actor& pActor)
{
	if( !IsOnLevelGrid(pActor) ) 
		return false;

	LevelElement* vLevelElement = SearchLevelElementFromActor(pActor, pOrientation);
	return vLevelElement == NULL || !vLevelElement->IsObstacle();
}

int Level::CanContinueThisWay(ActorOrientation pOrientation, Actor &pActor)
{
	if( !IsOnLevelGrid(pActor) ) 
	{
		switch(pOrientation)
			{
				case Right:
				case Left:
					return IsOnLevelGridY(pActor);
				case Down:
				case Up:
					return IsOnLevelGridX(pActor);
			}
			return true;
	}

	LevelElement* vLevelElement = SearchLevelElementFromActor(pActor, pOrientation);
	return vLevelElement == NULL || !vLevelElement->IsObstacle();
}
